Developer Documentation

QuickTime 4 API Documentation

QuickTime 4 Reference

QTWiredSprite.c Sample Code

This appendix includes sample code from QTWiredSprite.c . Note this is only a partial code listing. You can download the full sample code at QuickTime Web site at http://www.apple.com/quicktime/developers/samplecode.html#sprites .) It is also available on the QuickTime SDK.

For information on how to use this sample code in your application, see Chapter 2, "The Sprite Media Handler," and Chapter 3, "Authoring Wired Movies and Sprite Animations."

//////////
//
//  File:       QTWiredSprites.c
//
//  Contains:   QuickTime wired sprites support for QuickTime movies.
//

//  Written by: Sean Allen
//  Revised by: Chris Flick and Tim Monroe
//              Based (heavily!) on the existing MakeActionSpriteMovie.c
//              code written by Sean Allen.
//
//  Copyright:  © 1997-1999 by Apple Computer, Inc., all rights reserved.
//
//  Change History (most recent first):
//
//   <2>      03/26/98    rtm     made fixes for Windows compiles
//   <1>      03/25/98    rtm     first file; integrated existing code
//                                                  with shell framework
//  
//
//  This sample code creates a wired sprite movie containing one sprite
//track. The sprite track contains six sprites: two penguins and four
//buttons.
//  
//  The four buttons are initially invisible. When the mouse enters (or
//  "rolls over") a button, it appears.
//  When the mouse is clicked inside a button, its image changes to its ///
"pressed" image. When the mouse
//  is released, its image changes back to its "unpressed" image. If the //
mouse is released inside the button,
//  an action is triggered. The buttons perform the actions of go to
//  beginning of movie, step backward,
//  step forward, and go to end of movie.

//  
//  The first penguin shows all of the buttons when the mouse enters it,
//  and hides them when the mouse exits.
//  The first penguin is the only sprite that has properties that are
//  overriden by the override sprite samples.
//  These samples override its matrix (in order to move it) and its image
//  index (in order to make it "waddle").
//  
//  When the mouse is clicked on the second penguin, it changes its image
//  index to its "eyes closed" image.
//  When the mouse is released, it changes back to its normal image. This
//  makes it appear to blink when clicked on.
//  When the mouse is released over the penguin, several actions are
//  triggered. Both penguins' graphics states are
//  toggled between copyMode and blendMode, and the movie's rate is
//  toggled between zero and one.
//  
//  The second penguin moves once per second. This occurs whether the
//  movie's rate is currently zero or one,
//  because it is being triggered by a gated idle event. When the penguin
//  receives the idle event, it changes
//  its matrix using an action which uses min, max, delta, and wraparound
//  options.
//
//  The movie's looping mode is set to palindrome by a frame-loaded
//  action.
//
//  So, our general strategy is as follows (though perhaps not in the
//  order listed):
//
//      (1) Create a new movie file with a single sprite track.
//      (2) Assign the "no controller" movie controller to the movie.
//      (3) Set the sprite track's background color, idle event
//          frequency, and hasActions properties.
//      (4) Convert our PICT resources to animation codec images with
//          transparency.
//      (5) Create a key frame sample containing six sprites and all of
//          their shared images.
//      (6) Assign the sprites their initial property values.
//      (7) Create a frameLoaded event for the key frame.
//      (8) Create some override samples that override the matrix and
//          image index properties of the first penguin sprite.
//

//  NOTES:
//      
//  *** (1) ***
//  There are event types other that mouse related events (for instance,
//  Idle and FrameLoaded).
//  Idle events are independent of the movie's rate, and they can be
//  gated so they are send at most
//  every n ticks. In our sample movie, the second penguin moves when the
//  movie's rate is zero,
//  and moves only once per second because of the value of the sprite
//  track's idleEventFrequencey property.
//      
//  *** (2) ***
//  Multiple actions may be executed in response to a single event. In
//  our sample movie, rolling over
//  the first penguin shows and hides four different buttons.
//      
//  *** (3) ***
//  Actions may target any sprite or track in the movie. In our sample
//  movie, clicking on one penguin
//  changes the graphics mode of the other.
//      
//  *** (4) ***
//  Conditional and looping control structures are supported. In our
// sample movie, the second penguin
//  uses the "case statement" action.
//      
//  *** (5) ***
//  Sprite track variables that have not been set have a default value of
//  zero. (The second penguin's
//  conditional code relies on this.)
//      
//  *** (6) ***
//  Wired sprites were previously known as "action sprites". Don't let
//  the names of some of the utility
//  functions confuse you. We'll try to update the source code as time
//  permits.
//      
//  *** (7) ***
//  Penguins don't fly, but I hear they totally shred halfpipes on
//  snowboards.
//
//////////
// header files
#include "QTWiredSprites.h"


//////////
//
// QTWired_CreateWiredSpritesMovie
// Create a QuickTime movie containing a wired sprites track.
//
//////////

OSErr QTWired_CreateWiredSpritesMovie (void)
{
    short                   myResRefNum = 0;
    Movie                   myMovie = NULL;
    Track                   myTrack;
    Media                   myMedia;
    StandardFileReply       myReply;
    QTAtomContainer         mySample = NULL;
    QTAtomContainer         myActions = NULL;
    QTAtomContainer         myBeginButton, myPrevButton, myNextButton,
                            myEndButton;
    QTAtomContainer         myPenguinOne, myPenguinTwo,
                            myPenguinOneOverride;
    QTAtomContainer         myBeginActionButton, myPrevActionButton,
                            myNextActionButton, myEndActionButton;
    QTAtomContainer         myPenguinOneAction, myPenguinTwoAction;
    RGBColor                myKeyColor;
    Point                   myLocation;
    short                   isVisible, myLayer, myIndex, myResID, i,
                            myDelta;
    Boolean                 hasActions;
    long                    myFlags = createMovieFileDeleteCurFile |
                                        createMovieFileDontCreateResFile;
    OSType                  myType = FOUR_CHAR_CODE('none');
    UInt32                  myFrequency;
    QTAtom                  myEventAtom;
    long                    myLoopingFlags;
    ModifierTrackGraphicsModeRecord     myGraphicsMode;
    OSErr                   myErr = noErr;

    //////////
    //
    // create a new movie file and set its controller type
    //
    //////////

    // ask the user for the name of the new movie file
    StandardPutFile("\pSprite movie file name:", "\pSprite.mov",
                    &myReply);
    if (!myReply.sfGood)
        goto bail;

    // create a movie file for the destination movie
    myErr = CreateMovieFile(&myReply.sfFile, FOUR_CHAR_CODE('TVOD'), 0,
                            myFlags, &myResRefNum, &myMovie);
    if (myErr != noErr)
        goto bail;
    
    // select the "no controller" movie controller
    myType = EndianU32_NtoB(myType);
    SetUserDataItem(GetMovieUserData(myMovie), &myType, sizeof(myType),
                    kUserDataMovieControllerType, 1);
    
    //////////
    //
    // create the sprite track and media
    //
    //////////
    
    myTrack = NewMovieTrack(myMovie, ((long)kSpriteTrackWidth << 16),
                            ((long)kSpriteTrackHeight << 16), kNoVolume);
    myMedia = NewTrackMedia(myTrack, SpriteMediaType, kSpriteMediaTimeScale, NULL, 0);

    //////////
    //
    // create a key frame sample containing six sprites and all of their
    // shared images
    //
    //////////

    // create a new, empty key frame sample
    myErr = QTNewAtomContainer(&mySample);
    if (myErr != noErr)
        goto bail;

    myKeyColor.red = 0xffff;                        // white
    myKeyColor.green = 0xffff;
    myKeyColor.blue = 0xffff;

    // add images to the key frame sample
    AddPICTImageToKeyFrameSample(mySample, kGoToBeginningButtonUp,
                &myKeyColor, kGoToBeginningButtonUpIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToBeginningButtonDown,
                &myKeyColor, kGoToBeginningButtonDownIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToEndButtonUp, &myKeyColor,
                kGoToEndButtonUpIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToEndButtonDown,
                &myKeyColor, kGoToEndButtonDownIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToPrevButtonUp,
                &myKeyColor, kGoToPrevButtonUpIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToPrevButtonDown,
                &myKeyColor, kGoToPrevButtonDownIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToNextButtonUp,
                &myKeyColor, kGoToNextButtonUpIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToNextButtonDo

bail:
    if (mySample != NULL)
        QTDisposeAtomContainer(mySample);

    if (myBeginButton != NULL)
        QTDisposeAtomContainer(myBeginButton);  
            
    if (myPrevButton != NULL)
        QTDisposeAtomContainer(myPrevButton);
                
    if (myNextButton != NULL)
        QTDisposeAtomContainer(myNextButton);
                
    if (myEndButton != NULL)
        QTDisposeAtomContainer(myEndButton);        
        
    if (myResRefNum != 0)
        CloseMovieFile(myResRefNum);

    if (myMovie != NULL)
        DisposeMovie(myMovie);
        
    return(myErr);
}


//////////
//
// QTWired_AddPenguinTwoConditionalActions
// Add actions to the second penguin that transform him (her?) into a two
// state button
// that plays or pauses the movie.
//
// We are relying on the fact that a "GetVariable" for a variable ID
// which has never been set
// will return zero. If we needed a different default value, we could
// initialize it using the
// frameLoaded event.
//
// A higher-level description of the logic is:
//
//  On MouseUpInside
//   If (GetVariable(DefaultTrack, 1) = 0)
//   SetMovieRate(1)
//   SetSpriteGraphicsMode(DefaultSprite, { blend, grey } )
//   SetSpriteGraphicsMode(GetSpriteByID(DefaultTrack, 5),
//              { ditherCopy, white } )
//   SetVariable(DefaultTrack, 1, 1)
//   ElseIf (GetVariable(DefaultTrack, 1) = 1)
//   SetMovieRate(0)
//   SetSpriteGraphicsMode(DefaultSprite, { ditherCopy, white })
//   SetSpriteGraphicsMode(GetSpriteByID(DefaultTrack, 5),
//              { blend, grey })
//   SetVariable(DefaultTrack, 1, 0)
//   Endif
//  End
//
//////////

OSErr QTWired_AddPenguinTwoConditionalActions (QTAtomContainer
                                    theContainer, QTAtom theEventAtom)
{
    QTAtom          myNewActionAtom, myNewParamAtom, myConditionalAtom;
    QTAtom          myExpressionAtom, myOperatorAtom, myActionListAtom;
    short           myParamIndex, myConditionIndex, myOperandIndex;
    float           myConstantValue;
    QTAtomID        myVariableID;
    ModifierTrackGraphicsModeRecord     myBlendMode, myCopyMode;
    OSErr                               myErr = noErr;
    
    myBlendMode.graphicsMode = blend;
    myBlendMode.opColor.red = myBlendMode.opColor.green =
                        myBlendMode.opColor.blue = 0x8fff;      // grey

    myCopyMode.graphicsMode = ditherCopy;
    myCopyMode.opColor.red = myCopyMode.opColor.green =
                        myCopyMode.opColor.blue = 0xffff;       // white

    AddActionAtom(theContainer, theEventAtom, kActionCase,
                    &myNewActionAtom);
    
    myParamIndex = 1;
    AddActionParameterAtom(theContainer, myNewActionAtom, myParamIndex,
                            0, NULL, &myNewParamAtom);

    // first condition
    myConditionIndex = 1;
    AddConditionalAtom(theContainer, myNewParamAtom, myConditionIndex,
                        &myConditionalAtom);
    AddExpressionContainerAtomType(theContainer, myConditionalAtom,
                        &myExpressionAtom);
    AddOperatorAtom(theContainer, myExpressionAtom, kOperatorEqualTo,
                        &myOperatorAtom);

    myOperandIndex = 1;
    myConstantValue = kButtonStateOne;
    AddOperandAtom(theContainer, myOperatorAtom, kOperandConstant,
                        myOperandIndex, NULL, myConstantValue);

    myOperandIndex = 2;
    myVariableID = kPenguinStateVariableID;
    AddVariableOperandAtom(theContainer, myOperatorAtom, myOperandIndex,
                        0, NULL, 0, myVariableID);

    AddActionListAtom(theContainer, myConditionalAtom,
                        &myActionListAtom);
    AddMovieSetRateAction(theContainer, myActionListAtom, 0,
                        Long2Fix(1));
    AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0,
        NULL, 0, 0, NULL, &myBlendMode, NULL);
    AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0,
        NULL, 0, kTargetSpriteID, (void *)kPenguinOneSpriteID,
        &myCopyMode, NULL);
    AddSpriteTrackSetVariableAction(theContainer, myActionListAtom, 0,
        kPenguinStateVariableID, kButtonStateTwo, 0, NULL, 0);

                                    
    // second condition
    myConditionIndex = 2;
    AddConditionalAtom(theContainer, myNewParamAtom, myConditionIndex,
        &myConditionalAtom);
    AddExpressionContainerAtomType(theContainer, myConditionalAtom,
        &myExpressionAtom);
    AddOperatorAtom(theContainer, myExpressionAtom, kOperatorEqualTo,
        &myOperatorAtom);

    myOperandIndex = 1;
    myConstantValue = kButtonStateTwo;
    AddOperandAtom(theContainer, myOperatorAtom, kOperandConstant,
        myOperandIndex, NULL, myConstantValue);

    myOperandIndex = 2;
    myVariableID = kPenguinStateVariableID;
    AddVariableOperandAtom(theContainer, myOperatorAtom, myOperandIndex,
        0, NULL, 0, myVariableID);

    AddActionListAtom(theContainer, myConditionalAtom,
        &myActionListAtom);
    AddMovieSetRateAction(theContainer, myActionListAtom, 0,
        Long2Fix(0));
    AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0,
        NULL, 0, 0, NULL, &myCopyMode, NULL);
    AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0,
        NULL, 0, kTargetSpriteID, (void *)kPenguinOneSpriteID,
        &myBlendMode, NULL);
    AddSpriteTrackSetVariableAction(theContainer, myActionListAtom, 0,
        kPenguinStateVariableID, kButtonStateOne, 0, NULL, 0);


bail:
    return(myErr);
}


//////////
//
// QTWired_AddWraparoundMatrixOnIdle
// Add beginning, end, and change matrices to the specified atom
// container.
//
//////////

OSErr QTWired_AddWraparoundMatrixOnIdle (QTAtomContainer theContainer)
{
    MatrixRecord    myMinMatrix, myMaxMatrix, myDeltaMatrix;
    long            myFlags = kActionFlagActionIsDelta |
                                kActionFlagParameterWrapsAround;
    QTAtom          myActionAtom;
    OSErr           myErr = noErr;
    
    myMinMatrix.matrix[0][0] = myMinMatrix.matrix[0][1] =
        myMinMatrix.matrix[0][2] = EndianS32_NtoB(0xffffffff);
    myMinMatrix.matrix[1][0] = myMinMatrix.matrix[1][1] =
        myMinMatrix.matrix[1][2] = EndianS32_NtoB(0xffffffff);
    myMinMatrix.matrix[2][0] = myMinMatrix.matrix[2][1] =
        myMinMatrix.matrix[2][2] = EndianS32_NtoB(0xffffffff);

    myMaxMatrix.matrix[0][0] = myMaxMatrix.matrix[0][1] =
        myMaxMatrix.matrix[0][2] = EndianS32_NtoB(0x7fffffff);
    myMaxMatrix.matrix[1][0] = myMaxMatrix.matrix[1][1] =
        myMaxMatrix.matrix[1][2] = EndianS32_NtoB(0x7fffffff);
    myMaxMatrix.matrix[2][0] = myMaxMatrix.matrix[2][1] =
        myMaxMatrix.matrix[2][2] = EndianS32_NtoB(0x7fffffff);
    
    myMinMatrix.matrix[2][1] = EndianS32_NtoB(Long2Fix((1 *
        kSpriteTrackHeight / 4) - (kPenguinHeight / 2)));
    myMaxMatrix.matrix[2][1] = EndianS32_NtoB(Long2Fix((3 *
        kSpriteTrackHeight / 4) - (kPenguinHeight / 2)));

    SetIdentityMatrix(&myDeltaMatrix);
    myDeltaMatrix.matrix[2][1] = Long2Fix(1);
    
    // change location
    myErr = AddSpriteSetMatrixAction(theContainer,
            kParentAtomIsContainer, kQTEventIdle, 0, NULL, 0, 0, NULL,
            &myDeltaMatrix, &myActionAtom);
    if (myErr != noErr)
        goto bail;

    myErr = AddActionParameterOptions(theContainer, myActionAtom, 1,
            myFlags, sizeof(myMinMatrix), &myMinMatrix,
            sizeof(myMaxMatrix), &myMaxMatrix);

bail:
    return(myErr);
}

 

© 1999 Apple Computer, Inc.